/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// r_surf.c: surface-related refresh code

#include "quakedef.h"

#ifndef GL_RGBA4
#define	GL_RGBA4	0
#endif

int		        lightmap_textures;
float           lightmap_brightness;

unsigned		blocklights[18*18];

#define	BLOCK_WIDTH		128
#define	BLOCK_HEIGHT	128

#define	MAX_LIGHTMAPS	64
int			active_lightmaps;
glpoly_t	*lightmap_polys[MAX_LIGHTMAPS];
int			allocated[MAX_LIGHTMAPS][BLOCK_WIDTH];

// the lightmap texture data needs to be kept in
// main memory so texsubimage can update properly
byte		lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT];

/*
====================================================
PENTA: Added for brush model vertex array support...

	We first add vertices to a dynamically allocated buffer (malloc)
	at the end of level loading we copy that to a "quake" allocated
	buffer (on the Hunk)
====================================================
*/
mmvertex_t *globalVertexTable = NULL;
mmvertex_t *tempVertices = NULL;

int	tempVerticesSize = 0;
int	numTempVertices = 0;

int R_GetNextVertexIndex(void)
{
    return numTempVertices;
}
/*
Returns the index of the vertex the date was copied to...
*/
int R_AllocateVertexInTemp(vec3_t pos, float texture [2], float lightmap[2])
{
    int         i;
    mmvertex_t  *temp;

    if (!tempVertices)
    {
        tempVerticesSize = 512;
        tempVertices = malloc(tempVerticesSize * sizeof(mmvertex_t));
        numTempVertices = 0;
    }

    if (numTempVertices >= tempVerticesSize)
    {
        tempVerticesSize += 512;
        temp = malloc(tempVerticesSize * sizeof(mmvertex_t));

        if (!temp)
        {
            Sys_Error("R_AllocateVertexInTemp: malloc failed\n");
        }
        Q_memcpy(temp, tempVertices, (tempVerticesSize - 512) * sizeof(mmvertex_t));
        free(tempVertices);
        tempVertices = temp;
    }
    VectorCopy(pos, tempVertices[numTempVertices].position);

    for (i=0; i<2; i++)
    {
        tempVertices[numTempVertices].texture[i] = texture[i];
        tempVertices[numTempVertices].lightmap[i] = lightmap[i];
    }
    numTempVertices++;

    return numTempVertices-1;
}

void R_CopyVerticesToHunk(void)
{
    globalVertexTable = Hunk_Alloc(numTempVertices * sizeof(mmvertex_t));
    Q_memcpy(globalVertexTable, tempVertices, numTempVertices * sizeof(mmvertex_t));
    free(tempVertices);
    Con_Printf("Copied %i vertices to hunk\n", numTempVertices);

    tempVertices = NULL;
    tempVerticesSize = 0;
    numTempVertices = 0;
}

void R_EnableVertexTable(int fields)
{
    glVertexPointer(3, GL_FLOAT, VERTEXSIZE * sizeof(float), globalVertexTable);
    glEnableClientState(GL_VERTEX_ARRAY);

    if (fields & VERTEX_TEXTURE)
    {
        qglClientActiveTextureARB(GL_TEXTURE0_ARB);
        glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE * sizeof(float), (float *)(globalVertexTable) + 3);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    }

    if (fields & VERTEX_LIGHTMAP)
    {
        qglClientActiveTextureARB(GL_TEXTURE1_ARB);
        glTexCoordPointer(2, GL_FLOAT, VERTEXSIZE * sizeof(float), (float *)(globalVertexTable) + 5);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    }
    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
}

void R_DisableVertexTable(int fields)
{
    glVertexPointer(3, GL_FLOAT, 0, globalVertexTable);
    glDisableClientState(GL_VERTEX_ARRAY);

    if (fields & VERTEX_TEXTURE)
    {
        qglClientActiveTextureARB(GL_TEXTURE0_ARB);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    }

    if (fields & VERTEX_LIGHTMAP)
    {
        qglClientActiveTextureARB(GL_TEXTURE1_ARB);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    }
    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
}

/*
===============
R_BuildLightMap

Combine and scale multiple lightmaps into the 8.8 format in blocklights
===============
*/
void R_BuildLightMap (msurface_t *surf, byte *dest, int stride)
{
    int			smax, tmax;
    int			t;
    int			i, j, size;
    byte		*lightmap;
    unsigned	scale;
    int			maps;
    unsigned	*bl;

    smax = (surf->extents[0] >> 4) + 1;
    tmax = (surf->extents[1] >> 4) + 1;
    size = smax * tmax;
    lightmap = surf->samples;

// set to full bright if no light data
    if (r_fullbright.value || !cl.worldmodel->lightdata)
    {
        for (i=0 ; i<size ; i++)
        {
            blocklights[i] = 255*256;
        }
        goto store;
    }

// clear to no light
	memset (blocklights, 0, size * sizeof(int));

// add all the lightmaps
    if (lightmap)
    {
        for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; maps++)
        {
            scale = d_lightstylevalue[surf->styles[maps]];

            for (i=0 ; i<size ; i++)
            {
                blocklights[i] += lightmap[i] * scale;
            }
            lightmap += size;	// skip to next lightmap
        }
    }

// bound, invert, and shift
store:
    bl = blocklights;

    for (i=0 ; i<tmax ; i++, dest += stride)
    {
        for (j=0 ; j<smax ; j++)
        {
            t = *bl++;
            t >>= 7;

            if (t > 255)
            {
                t = 255;
            }
            dest[j] = t;
        }
    }
}


/*
===============
R_TextureAnimation

Returns the proper texture for a given time and base texture
===============
*/
texture_t *R_TextureAnimation (texture_t *base)
{
    // MH - spelled relative correctly
    int		relative;
    int		count;

    // store the initial base in the event of an error
    texture_t *errorbase = base;

    if (currententity->frame)
    {
        if (base->alternate_anims)
        {
            base = base->alternate_anims;
        }
    }

    if (!base->anim_total)
    {
        return base;
    }
    relative = (int)(cl.time * 10) % base->anim_total;

    count = 0;

    while (base->anim_min > relative || base->anim_max <= relative)
    {
        base = base->anim_next;

        // if any error occurs, just put back the base texture
        if (!base) return errorbase;
        if (++count > 100) return errorbase;
    }
    return base;
}

/*
=============================================================

	BRUSH MODELS

=============================================================
*/

extern	int		solidskytexture;
extern	int		alphaskytexture;
extern	float	speedscale;		// for top sky and bottom sky

/* NV_register_combiners command function pointers
PENTA: I put them here because mtex functions are above it, and I hadent't any ispiration for some other place.
*/
PFNGLCOMBINERPARAMETERFVNVPROC qglCombinerParameterfvNV = NULL;
PFNGLCOMBINERPARAMETERIVNVPROC qglCombinerParameterivNV = NULL;
PFNGLCOMBINERPARAMETERFNVPROC qglCombinerParameterfNV = NULL;
PFNGLCOMBINERPARAMETERINVPROC qglCombinerParameteriNV = NULL;
PFNGLCOMBINERINPUTNVPROC qglCombinerInputNV = NULL;
PFNGLCOMBINEROUTPUTNVPROC qglCombinerOutputNV = NULL;
PFNGLFINALCOMBINERINPUTNVPROC qglFinalCombinerInputNV = NULL;
PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC qglGetCombinerInputParameterfvNV = NULL;
PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC qglGetCombinerInputParameterivNV = NULL;
PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC qglGetCombinerOutputParameterfvNV = NULL;
PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC qglGetCombinerOutputParameterivNV = NULL;
PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC qglGetFinalCombinerInputParameterfvNV = NULL;
PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC qglGetFinalCombinerInputParameterivNV = NULL;


PFNGLTEXIMAGE3DEXT qglTexImage3DEXT = NULL;
PFNGLDEPTHBOUNDSNV qglDepthBoundsNV = NULL;

PFNGLACTIVETEXTUREARBPROC qglActiveTextureARB = NULL;
PFNGLCLIENTACTIVETEXTUREARBPROC qglClientActiveTextureARB = NULL;
PFNGLMULTITEXCOORD1FARBPROC qglMultiTexCoord1fARB = NULL;
PFNGLMULTITEXCOORD2FARBPROC qglMultiTexCoord2fARB = NULL;
PFNGLMULTITEXCOORD2FVARBPROC qglMultiTexCoord2fvARB = NULL;
PFNGLMULTITEXCOORD3FARBPROC qglMultiTexCoord3fARB = NULL;
PFNGLMULTITEXCOORD3FVARBPROC qglMultiTexCoord3fvARB = NULL;
PFNGLAREPROGRAMSRESIDENTNVPROC qglAreProgramsResidentNV = NULL;
PFNGLBINDPROGRAMNVPROC qglBindProgramNV = NULL;
PFNGLDELETEPROGRAMSNVPROC qglDeleteProgramsNV = NULL;
PFNGLEXECUTEPROGRAMNVPROC qglExecuteProgramNV = NULL;
PFNGLGENPROGRAMSNVPROC qglGenProgramsNV = NULL;
PFNGLGETPROGRAMPARAMETERDVNVPROC qglGetProgramParameterdvNV = NULL;
PFNGLGETPROGRAMPARAMETERFVNVPROC qglGetProgramParameterfvNV = NULL;
PFNGLGETPROGRAMIVNVPROC qglGetProgramivNV = NULL;
PFNGLGETPROGRAMSTRINGNVPROC qglGetProgramStringNV = NULL;
PFNGLGETTRACKMATRIXIVNVPROC qglGetTrackMatrixivNV = NULL;
PFNGLGETVERTEXATTRIBDVNVPROC qglGetVertexAttribdvNV = NULL;
PFNGLGETVERTEXATTRIBFVNVPROC qglGetVertexAttribfvNV = NULL;
PFNGLGETVERTEXATTRIBIVNVPROC qglGetVertexAttribivNV = NULL;
PFNGLGETVERTEXATTRIBPOINTERVNVPROC qglGetVertexAttribPointervNV = NULL;
PFNGLISPROGRAMNVPROC qglIsProgramNV = NULL;
PFNGLLOADPROGRAMNVPROC qglLoadProgramNV = NULL;
PFNGLPROGRAMPARAMETER4DNVPROC qglProgramParameter4dNV = NULL;
PFNGLPROGRAMPARAMETER4DVNVPROC qglProgramParameter4dvNV = NULL;
PFNGLPROGRAMPARAMETER4FNVPROC qglProgramParameter4fNV = NULL;
PFNGLPROGRAMPARAMETER4FVNVPROC qglProgramParameter4fvNV = NULL;
PFNGLPROGRAMPARAMETERS4DVNVPROC qglProgramParameters4dvNV = NULL;
PFNGLPROGRAMPARAMETERS4FVNVPROC qglProgramParameters4fvNV = NULL;
PFNGLREQUESTRESIDENTPROGRAMSNVPROC qglRequestResidentProgramsNV = NULL;
PFNGLTRACKMATRIXNVPROC qglTrackMatrixNV = NULL;
PFNGLVERTEXATTRIBPOINTERNVPROC qglVertexAttribPointerNV = NULL;
PFNGLVERTEXATTRIB1DNVPROC qglVertexAttrib1dNV = NULL;
PFNGLVERTEXATTRIB1DVNVPROC qglVertexAttrib1dvNV = NULL;
PFNGLVERTEXATTRIB1FNVPROC qglVertexAttrib1fNV = NULL;
PFNGLVERTEXATTRIB1FVNVPROC qglVertexAttrib1fvNV = NULL;
PFNGLVERTEXATTRIB1SNVPROC qglVertexAttrib1sNV = NULL;
PFNGLVERTEXATTRIB1SVNVPROC qglVertexAttrib1svNV = NULL;
PFNGLVERTEXATTRIB2DNVPROC qglVertexAttrib2dNV = NULL;
PFNGLVERTEXATTRIB2DVNVPROC qglVertexAttrib2dvNV = NULL;
PFNGLVERTEXATTRIB2FNVPROC qglVertexAttrib2fNV = NULL;
PFNGLVERTEXATTRIB2FVNVPROC qglVertexAttrib2fvNV = NULL;
PFNGLVERTEXATTRIB2SNVPROC qglVertexAttrib2sNV = NULL;
PFNGLVERTEXATTRIB2SVNVPROC qglVertexAttrib2svNV = NULL;
PFNGLVERTEXATTRIB3DNVPROC qglVertexAttrib3dNV = NULL;
PFNGLVERTEXATTRIB3DVNVPROC qglVertexAttrib3dvNV = NULL;
PFNGLVERTEXATTRIB3FNVPROC qglVertexAttrib3fNV = NULL;
PFNGLVERTEXATTRIB3FVNVPROC qglVertexAttrib3fvNV = NULL;
PFNGLVERTEXATTRIB3SNVPROC qglVertexAttrib3sNV = NULL;
PFNGLVERTEXATTRIB3SVNVPROC qglVertexAttrib3svNV = NULL;
PFNGLVERTEXATTRIB4DNVPROC qglVertexAttrib4dNV = NULL;
PFNGLVERTEXATTRIB4DVNVPROC qglVertexAttrib4dvNV = NULL;
PFNGLVERTEXATTRIB4FNVPROC qglVertexAttrib4fNV = NULL;
PFNGLVERTEXATTRIB4FVNVPROC qglVertexAttrib4fvNV = NULL;
PFNGLVERTEXATTRIB4SNVPROC qglVertexAttrib4sNV = NULL;
PFNGLVERTEXATTRIB4SVNVPROC qglVertexAttrib4svNV = NULL;
PFNGLVERTEXATTRIB4UBVNVPROC qglVertexAttrib4ubvNV = NULL;
PFNGLVERTEXATTRIBS1DVNVPROC qglVertexAttribs1dvNV = NULL;
PFNGLVERTEXATTRIBS1FVNVPROC qglVertexAttribs1fvNV = NULL;
PFNGLVERTEXATTRIBS1SVNVPROC qglVertexAttribs1svNV = NULL;
PFNGLVERTEXATTRIBS2DVNVPROC qglVertexAttribs2dvNV = NULL;
PFNGLVERTEXATTRIBS2FVNVPROC qglVertexAttribs2fvNV = NULL;
PFNGLVERTEXATTRIBS2SVNVPROC qglVertexAttribs2svNV = NULL;
PFNGLVERTEXATTRIBS3DVNVPROC qglVertexAttribs3dvNV = NULL;
PFNGLVERTEXATTRIBS3FVNVPROC qglVertexAttribs3fvNV = NULL;
PFNGLVERTEXATTRIBS3SVNVPROC qglVertexAttribs3svNV = NULL;
PFNGLVERTEXATTRIBS4DVNVPROC qglVertexAttribs4dvNV = NULL;
PFNGLVERTEXATTRIBS4FVNVPROC qglVertexAttribs4fvNV = NULL;
PFNGLVERTEXATTRIBS4SVNVPROC qglVertexAttribs4svNV = NULL;
PFNGLVERTEXATTRIBS4UBVNVPROC qglVertexAttribs4ubvNV = NULL;

// <AWE> There are some diffs with the function parameters. wgl stuff not present with MacOS X. -DC- and SDL
#if defined (__APPLE__) || defined (MACOSX) || defined (SDL) || defined (__glx__)

PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC qglFlushVertexArrayRangeAPPLE  = NULL;
PFNGLVERTEXARRAYRANGEAPPLEPROC qglVertexArrayRangeAPPLE  = NULL;

#else

PFNGLFLUSHVERTEXARRAYRANGENVPROC qglFlushVertexArrayRangeNV  = NULL;
PFNGLVERTEXARRAYRANGENVPROC glVertexArrayRangeNV  = NULL;
PFNWGLALLOCATEMEMORYNVPROC wglAllocateMemoryNV  = NULL;
PFNWGLFREEMEMORYNVPROC wglFreeMemoryNV  = NULL;

#endif /* __APPLE__ || MACOSX */

/*
================
R_RenderBrushPoly

PENTA: Modifications
================
*/
void R_RenderBrushPoly (msurface_t *fa)
{
    glpoly_t	*p;
    texture_t	*t = R_TextureAnimation (fa->texinfo->texture);

    R_EnableVertexTable(VERTEX_TEXTURE | VERTEX_LIGHTMAP);

    if (fa->flags & SURF_DRAWSKY)
    {
        // warp texture, no lightmaps
        EmitBothSkyLayers (fa);
        return;
    }
    GL_SelectTexture (GL_TEXTURE0_ARB);
    GL_Bind (t->gl_texturenum);

    if (fa->flags & SURF_DRAWTURB)
    {
        // warp texture, no lightmaps
        EmitWaterPolys (fa);
        return;
    }
    GL_SelectTexture (GL_TEXTURE1_ARB);
    GL_Bind (lightmap_textures + fa->lightmaptexturenum);

    // set normals (precalculated)
    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer(GL_FLOAT, 0, fa->truenormal);

    // this is the lightmap brightness and should only be used here not every fucking where else !!!
    glColor3f(lightmap_brightness, lightmap_brightness, lightmap_brightness);

    for (p = fa->polys; p ; p = p->next)
    {
        glDrawArrays(GL_TRIANGLE_FAN, p->firstvertex, p->numverts);
    }
    glColor3f(1, 1, 1);

    R_DisableVertexTable(VERTEX_TEXTURE | VERTEX_LIGHTMAP);
    glDisableClientState(GL_NORMAL_ARRAY);
}

/*
================
R_RenderBrushPolyLuma

PENTA:
================
*/
void R_RenderBrushPolyLuma (msurface_t *fa)
{
    glpoly_t    *p;
    texture_t   *t = R_TextureAnimation (fa->texinfo->texture);

    if (fa->flags & SURF_DRAWSKY) return;
    if (fa->flags & SURF_DRAWTURB) return;

    R_EnableVertexTable(VERTEX_TEXTURE);

    // its a luma texture
    if (t->gl_lumatexturenum)
    {
        glDepthMask (GL_FALSE);
        glFogfv(GL_FOG_COLOR, fog_blank);
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
        GL_DisableTMU(GL_TEXTURE1_ARB);
        GL_SelectTexture(GL_TEXTURE0_ARB);

        GL_Bind (t->gl_lumatexturenum);

        for (p = fa->polys; p ; p = p->next)
        {
            glDrawArrays(GL_TRIANGLE_FAN, p->firstvertex, p->numverts);
        }
        GL_EnableTMU(GL_TEXTURE1_ARB);
        GL_SelectTexture(GL_TEXTURE0_ARB);
        GL_SelectTexture(GL_TEXTURE1_ARB);
        glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glDisable(GL_BLEND);
        glFogfv(GL_FOG_COLOR, fog_color);
        glDepthMask (GL_TRUE);
    }
    R_DisableVertexTable(VERTEX_TEXTURE);
}

/*
=============
PENTA:

Binds a water shader
=============
*/
static qboolean R_OverrideFluidTex(msurface_t *s)
{
    if (gl_watershader.value < 1) return false;
    if (r_wateralpha.value == 1) return false;

    if (s->flags & SURF_WATER)
    {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        GL_Bind(newwatertex);
        glScalef(0.5f, 0.5f, 1.0f);
        glTranslatef(-0.025f * realtime, -0.025f * realtime, 0.0f);

        GL_EnableMultitexture();

        GL_Bind(newwatertex);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
        glTranslatef(0.05f * realtime, 0.05f * realtime, 0.0f);
        glScalef(0.25f, 0.25f, 1.0f);
        return true;
    }

    if (s->flags & SURF_TELE)
    {
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
        GL_Bind(newteletex);
        glTranslatef(realtime, 0.5f * realtime, 0.0f);

        GL_EnableMultitexture();

        GL_Bind(newteletex);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
        glTranslatef(-realtime, 0.5f * realtime, 0.0f);
        glFogfv(GL_FOG_COLOR, fog_blank);
        return true;
    }

    if (s->flags & SURF_LAVA)
    {
        //lava is never transparent
        glDisable(GL_BLEND);
        GL_Bind(newlavatex);
        glColor4f(1, 1, 1, 1);
        return true;
    }

    if (s->flags & SURF_SLIME)
    {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        GL_Bind(newslimetex);
        glScalef(0.5f, 0.5f, 1.0f);
        glTranslatef(-0.05f * realtime, -0.05f * realtime, 0.0f);

        GL_EnableMultitexture();

        GL_Bind(newslimetex);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
        glScalef(0.25f, 0.25f, 1.0f);
        glTranslatef(0.05f * realtime, 0.05f * realtime, 0.0f);
        return true;
    }

    if (s->flags & SURF_GLASS)
    {
        glEnable(GL_BLEND);
        glBlendFunc(GL_DST_COLOR, GL_ONE);
        GL_Bind(newenvmap);
        glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
        glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
        glEnable(GL_TEXTURE_GEN_S);
        glEnable(GL_TEXTURE_GEN_T);
        glFogfv(GL_FOG_COLOR, fog_blank);
        return true;
    }
    return false;
}

/*
================
R_DrawWaterSurfaces

PENTA: Modifications
================
*/
void R_DrawWaterSurfaces (void)
{
    int			i;
    msurface_t	*s;
    texture_t	*t;

    //
    // go back to the world matrix
    //
    glLoadMatrixf (r_world_matrix);

    if (r_wateralpha.value < 1.0)
    {
		glDepthMask (GL_FALSE);

        if (gl_watershader.value < 1.0)
        {
            glEnable (GL_BLEND);
            glColor4f (1, 1, 1, r_wateralpha.value);
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        }
        else
        {
            glEnable (GL_BLEND);
            glColor4f (1, 1, 1, r_wateralpha.value);
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glMatrixMode(GL_TEXTURE);
            glLoadIdentity();
        }
    }
    else
    {
        glColor4f (1,1,1,1);
    }

    for (i=0 ; i<cl.worldmodel->numtextures ; i++)
    {
        t = cl.worldmodel->textures[i];
        if (!t) continue;
        s = t->texturechain;
        if (!s) continue;

        if (!(s->flags & SURF_DRAWTURB)) continue;
        if ((s->flags & SURF_MIRROR)) continue;

        // set modulate mode explicitly
        if (!R_OverrideFluidTex(s))
        {
            GL_Bind (t->gl_texturenum);
        }

        // draw the water polys
        for (/**/ ; s ; s = s->texturechain)
        {
            EmitWaterPolys (s);
        }

        //PENTA: only translate back if we use shaders
        if ((gl_watershader.value == 1) && (r_wateralpha.value < 1) )
        {
            glLoadIdentity();
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            GL_DisableMultitexture() ;
            glLoadIdentity();
            glDisable(GL_TEXTURE_GEN_S);
            glDisable(GL_TEXTURE_GEN_T);
            glFogfv(GL_FOG_COLOR, fog_color);
			glDepthMask (GL_FALSE);
        }
    }

    if (r_wateralpha.value < 1.0)
    {
		glDepthMask (GL_TRUE);

        if (gl_watershader.value < 1.0)
        {
            glColor4f (1, 1, 1, r_wateralpha.value);
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
            glDisable(GL_BLEND);
        }
        else
        {
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
            glColor4f (1, 1, 1, 1);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
            glDisable (GL_BLEND);
            glMatrixMode(GL_MODELVIEW);
        }
    }
    else
    {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
}

/*
================
R_DrawTextureChains

PENTA: Modifications
================
*/
void R_DrawTextureChains (void)
{
    int			i;
    msurface_t	*s;
    texture_t	*t;

    GL_DisableMultitexture();
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    GL_EnableMultitexture();

    if (sh_colormaps.value)
    {
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    }
    else
    {
        //No colormaps: Color maps are bound on tmu 0 so disable it
        //and let tmu 1 modulate itself with the light map brightness
        glDisable(GL_REGISTER_COMBINERS_NV);
        GL_DisableTMU(GL_TEXTURE0_ARB);
        GL_SelectTexture(GL_TEXTURE1_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
        glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
    }

    if (gl_overbright.value > 0)
    {
        glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
        glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
        glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
        glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);

        switch ((int) gl_overbright.value)
        {
        case 1:
            glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
            break;

        case 2:
            glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 4);
            break;

         default:
            glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);
            break;
        }
    }

    if (gl_wireframe.value)
    {
        GL_DisableTMU(GL_TEXTURE0_ARB);
        GL_DisableTMU(GL_TEXTURE1_ARB);
    }

//
// world textures
//
    for (i=0 ; i<cl.worldmodel->numtextures ; i++)
    {
        t = cl.worldmodel->textures[i];
        if (!t) continue;
        s = t->texturechain;
        if (!s)	continue;

        // works perfectly fine
        // so why they changed it is beyond me...
		if (i == skytexturenum)
        {
			R_DrawSkyChain (s);
		}
        else
        {
            if (s->flags & SURF_DRAWTURB) continue;

            for (/**/ ; s ; s = s->texturechain)
            {
                R_RenderBrushPoly (s);
                c_brush_polys++;
            }
        }
    }

    /* back to replace texenv for luma */
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

//
// luma textures
//
    for (i=0 ; i<cl.worldmodel->numtextures ; i++)
    {
        t = cl.worldmodel->textures[i];
        if (!t) continue;
        s = t->texturechain;
        if (!s)	continue;

        // no luma in sky so its not correct but doesnt hurt any.
		if (i == skytexturenum)
        {
			R_DrawSkyChain (s);
		}
        else
        {
            if (s->flags & SURF_DRAWTURB) continue;

            for (/**/ ; s ; s = s->texturechain)
            {
                R_RenderBrushPolyLuma (s);
                c_brush_polys++;
            }
        }
    }
    GL_DisableMultitexture();

    if (gl_overbright.value > 0)
    {
        glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);
    }
}

/*
=================
R_ClearTextureChains

Clears Texture Chains
=================
*/
void R_ClearTextureChains(void)
{
	int         i;
	msurface_t  *s;
	texture_t   *t;

	for (i=0 ; i<cl.worldmodel->numtextures ; i++)
	{
		t = cl.worldmodel->textures[i];
		if (!t) continue;
		s = t->texturechain;
		if (!s) continue;

		t->texturechain = NULL;
	}
}

/*
=================
R_DrawBrushModel

PENTA: Modifications
=================
*/
void R_DrawBrushModel (entity_t *e)
{
    vec3_t		mins, maxs;
    int			i;
    float       dotp;
    msurface_t	*psurf;
    model_t		*clmodel;
    qboolean	rotated;

    currententity = e;
    currenttexture = -1;
    clmodel = e->model;

    if (e->angles[0] || e->angles[1] || e->angles[2])
    {
        rotated = true;

        if (R_CullSphere(e->origin, clmodel->radius, 15)) return;
    }
    else
    {
        rotated = false;

        VectorAdd (e->origin, clmodel->mins, mins);
        VectorAdd (e->origin, clmodel->maxs, maxs);

        if (R_CullBox (mins, maxs, 15)) return;
    }
    memset (lightmap_polys, 0, sizeof(lightmap_polys));

    VectorSubtract (r_refdef.vieworg, e->origin, modelorg);

    if (rotated)
    {
        vec3_t	temp;
        vec3_t	forward, right, up;

        VectorCopy (modelorg, temp);
        AngleVectors (e->angles, forward, right, up);
        modelorg[0] = DotProduct (temp, forward);
        modelorg[1] = -DotProduct (temp, right);
        modelorg[2] = DotProduct (temp, up);
    }
    glPushMatrix ();
    e->angles[0] = -e->angles[0];	// stupid quake bug
    R_RotateForEntity (e);
    e->angles[0] = -e->angles[0];	// stupid quake bug

    //Draw model with specified ambient color
    GL_DisableMultitexture();
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    GL_EnableMultitexture();

    if (sh_colormaps.value)
    {
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    }
    else
    {
        //No colormaps: Color maps are bound on tmu 0 so disable it
        //and let tmu 1 modulate itself with the light map brightness
        glDisable(GL_REGISTER_COMBINERS_NV);
        GL_DisableTMU(GL_TEXTURE0_ARB);
        GL_SelectTexture(GL_TEXTURE1_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
        glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
    }

    if (gl_overbright.value > 0)
    {
        glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
        glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
        glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
        glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
        glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);

        switch ((int) gl_overbright.value)
        {
        case 1:
            glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
            break;

        case 2:
            glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 4);
            break;

         default:
            glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);
            break;
        }
    }

    if (gl_wireframe.value)
    {
        GL_DisableTMU(GL_TEXTURE0_ARB);
        GL_DisableTMU(GL_TEXTURE1_ARB);
    }
    c_brush_polys += clmodel->nummodelsurfaces;

//
// draw texture
//
    for (i=0, psurf = &clmodel->surfaces[clmodel->firstmodelsurface] ; i<clmodel->nummodelsurfaces ; i++, psurf++)
    {
		// don't bother adding these
		if (psurf->flags & (SURF_DRAWSKY | SURF_DRAWTURB)) continue;

		// get a dot product for backplane testing
		switch (psurf->plane->type)
		{
		case PLANE_X:
			dotp = modelorg[0] - psurf->plane->dist;
			break;

		case PLANE_Y:
			dotp = modelorg[1] - psurf->plane->dist;
			break;

		case PLANE_Z:
			dotp = modelorg[2] - psurf->plane->dist;
			break;

		default:
			dotp = DotProduct (modelorg, psurf->plane->normal) - psurf->plane->dist;
			break;
		}

        if (!(((psurf->flags & SURF_PLANEBACK) && (dotp < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dotp > BACKFACE_EPSILON)))) continue;

        R_RenderBrushPoly (psurf);
    }

    /* back to replace texenv for luma */
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

//
// draw luma
//
    for (i=0, psurf = &clmodel->surfaces[clmodel->firstmodelsurface] ; i<clmodel->nummodelsurfaces ; i++, psurf++)
    {
		// don't bother adding these
		if (psurf->flags & (SURF_DRAWSKY | SURF_DRAWTURB)) continue;

		// get a dot product for backplane testing
		switch (psurf->plane->type)
		{
		case PLANE_X:
			dotp = modelorg[0] - psurf->plane->dist;
			break;

		case PLANE_Y:
			dotp = modelorg[1] - psurf->plane->dist;
			break;

		case PLANE_Z:
			dotp = modelorg[2] - psurf->plane->dist;
			break;

		default:
			dotp = DotProduct (modelorg, psurf->plane->normal) - psurf->plane->dist;
			break;
		}

        if (!(((psurf->flags & SURF_PLANEBACK) && (dotp < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dotp > BACKFACE_EPSILON)))) continue;

        R_RenderBrushPolyLuma (psurf);
    }
    GL_DisableMultitexture();

    if (gl_overbright.value > 0)
    {
        glTexEnvf (GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);
    }
    glPopMatrix ();
}

/*
=============================================================

	WORLD MODEL

=============================================================
*/

/*
================
R_RecursiveWorldNode

PENTA: Modifications
================
*/
void R_RecursiveWorldNode (mnode_t *node, int clipflags)
{
    int			c, side, sidebit;
    mplane_t	*plane;
    msurface_t	*surf, **mark;
    mleaf_t		*pleaf;
    double		dot;

    if (node->contents == CONTENTS_SOLID) return;		// solid
    if (node->visframe != r_visframecount) return;

    // node culling blatantly stolen from software code since its godly :)
    for (c = 0, plane = frustum ; c < 4 ; c++, plane++)
    {
        if (!(clipflags & (1 << c))) continue;	// don't need to clip against it

        if (BoxOnPlaneSide(node->minmaxs, node->minmaxs + 3, plane) == 2) return;

        if (BoxOnPlaneSide(node->minmaxs, node->minmaxs + 3, plane) == 1)
        {
            clipflags &= ~(1 << c);				// node is entirely on screen
        }
    }

    if (mirror)
    {
        if ((mirror_clipside == BoxOnPlaneSide(node->minmaxs, node->minmaxs + 3, mirror_plane))) return;
        if (BoxOnPlaneSide(node->minmaxs, node->minmaxs + 3, &mirror_far_plane) == 1) return;
    }


// if a leaf node, draw stuff
    if (node->contents < 0)
    {
        pleaf = (mleaf_t *)node;
        mark = pleaf->firstmarksurface;
        c = pleaf->nummarksurfaces;

        if (c)
        {
            do
            {
                (*mark)->visframe = r_framecount;
                mark++;
            }
            while (--c);
        }

        // deal with model fragments in this leaf
        // steek de statishe moddellen voor dit leafy in de lijst
        // waarom zo ingewikkeld doen via efrags is nog niet helemaal duidelijk
        if (pleaf->efrags)
        {
            R_StoreEfrags (&pleaf->efrags);
        }
        return;
    }

// node is just a decision point, so go down the apropriate sides
// find which side of the node we are on
    plane = node->plane;

    switch (plane->type)
    {
    case PLANE_X:
        dot = modelorg[0] - plane->dist;
        break;

    case PLANE_Y:
        dot = modelorg[1] - plane->dist;
        break;

    case PLANE_Z:
        dot = modelorg[2] - plane->dist;
        break;

    default:
        dot = DotProduct (modelorg, plane->normal) - plane->dist;
        break;
    }

    // find back facing verts
    side = (dot >= 0) ? 0 : 1;
    sidebit = (!side) ? 0 : SURF_PLANEBACK;

// recurse down the children, front side first
    R_RecursiveWorldNode (node->children[side], clipflags);

// draw stuff
    if (node->numsurfaces)
    {
        for (surf = cl.worldmodel->surfaces + node->firstsurface, c = node->numsurfaces ; c ; c--, surf++)
        {
            if (surf->visframe != r_framecount) continue;

            // which side are we on?
            if ((surf->flags & SURF_PLANEBACK) != sidebit) continue;		// wrong side

            // hmm this is making me bald :S
            if (surf->flags & SURF_MIRROR)
            {
                //PENTA the  SURF_UNDERWATER check is needed so that we dont draw glass
                //twice since we designed it as a water block we would render two poly's with the
                //mirror otherwice
                if (!(surf->flags & (SURF_UNDERWATER & SURF_GLASS)))
                {
                    // dont allocate mirrors on turb textures
                    if (!(surf->flags & SURF_DRAWTURB) && (!mirror))
                    {
                        R_AllocateMirror(surf);
                    }
                }

                //destroy visframe
                surf->visframe = 0;
                continue;
            }

            if (surf->flags & (SURF_DRAWTURB & SURF_MIRROR)) continue;
            if ((surf->flags & SURF_DRAWTURB) && (mirror)) continue;

            // add the surface to the proper texture chain
            surf->texturechain = surf->texinfo->texture->texturechain;
            surf->texinfo->texture->texturechain = surf;

            // add the poly to the proper lightmap chain
            if (!(surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB)))
            {
                surf->polys->chain = lightmap_polys[surf->lightmaptexturenum];
                lightmap_polys[surf->lightmaptexturenum] = surf->polys;
            }
        }
    }

// recurse down the back side
    R_RecursiveWorldNode (node->children[!side], clipflags);
}

/*
=============
R_InitDrawWorld

Make sure everyting is set up correctly to draw
the world model.

PENTA: Modifications
=============
*/
void R_InitDrawWorld (void)
{
    entity_t	ent;

    memset (&ent, 0, sizeof(ent));
    ent.model = cl.worldmodel;

    VectorCopy (r_refdef.vieworg, modelorg);

    currententity = &ent;
    currenttexture = -1;

    glColor3f (1, 1, 1);
    memset (lightmap_polys, 0, sizeof(lightmap_polys));

    R_ClearTextureChains ();

    R_ClearSkyBox ();

    //mark visible polygons/ents
    R_RecursiveWorldNode (cl.worldmodel->nodes, 15);

    R_DrawSkyBox ();
}

/*
=============
R_WorldMultiplyTextures

Once we have drawn the lightmap in the depth buffer multiply the textures over it
PENTA: New
=============
*/
void R_WorldMultiplyTextures(void)
{
    R_DrawTextureChains ();
}

/*
===============
R_MarkLeaves


Markeer visible leafs voor dit frame
aangeroepen vanuit render scene voor alle
ander render code
(hmm handig hiernaa kunnen we dus al van de vis voor de camera
gebruik maken en toch nog stuff doen)

PENTA: Modifications

===============
*/
void R_MarkLeaves (void)
{
    byte	*vis;
    mnode_t	*node;
    int		i, j;
    byte	solid[4096];

    r_visframecount++;//begin nieuwe timestamp
    r_oldviewleaf = r_viewleaf;

    if (r_novis.value)
    {
        vis = solid;
        memset (solid, 0xff, (cl.worldmodel->numleafs + 7) >> 3);
    }
	else
	{
		vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel);

		if (r_viewleaf2)
		{
			int		    count;
			unsigned	*src, *dest;

			// merge visibility data for two leafs
			count = (cl.worldmodel->numleafs + 7) >> 3;
			memcpy (solid, vis, count);
			src = (unsigned *)Mod_LeafPVS (r_viewleaf2, cl.worldmodel);
			dest = (unsigned *)solid;
			count = (count + 3) >> 2;

			for (i = 0 ; i < count ; i++)
            {
				*dest++ |= *src++;
			}
			vis = solid;
		}
	}

    for (j=0 ; j<cl.worldmodel->numleafs ; j++)//overloop alle leafs
    {
        if (vis[j >> 3] & (1 << (j & 7))) // staat leaf i's vis bit op true ?
        {
            node = (mnode_t *)&cl.worldmodel->leafs[j + 1];

            do
            {
                if (node->visframe == r_visframecount) break;
                node->visframe = r_visframecount;//zet node zijn timestamp voor dit frame
                node = node->parent;//mark parents as vis
            } while (node);
        }
    }
}

/*
=============================================================================

  LIGHTMAP ALLOCATION

=============================================================================
*/

// returns a texture number and the position inside it
int AllocBlock (int w, int h, int *x, int *y)
{
    int		i, j;
    int		best, best2;
    int		texnum;

    for (texnum=0 ; texnum<MAX_LIGHTMAPS ; texnum++)
    {
        best = BLOCK_HEIGHT;

        for (i=0 ; i<BLOCK_WIDTH-w ; i++)
        {
            best2 = 0;

            for (j=0 ; j<w ; j++)
            {
                if (allocated[texnum][i+j] >= best) break;

                if (allocated[texnum][i+j] > best2)
                {
                    best2 = allocated[texnum][i+j];
                }
            }

            if (j == w)
            {
                // this is a valid spot
                *x = i;
                *y = best = best2;
            }
        }

        if (best + h > BLOCK_HEIGHT) continue;

        for (i=0 ; i<w ; i++)
        {
            allocated[texnum][*x + i] = best + h;
        }
        return texnum;
    }
    Sys_Error ("AllocBlock: full");

    return 0;
}

//PENTA: temporaly storage for polygons that use an edge
typedef struct
{
    int used;				//how many polygons use this edge
    glpoly_t	*poly[2];	//pointer to the polygons who use this edge
} temp_connect_t;

temp_connect_t  *tempEdges;
mvertex_t	    *r_pcurrentvertbase;
model_t		    *currentmodel;

/*
================
PENTA:
SetupSurfaceConnectivity

Setup the neighour pointers of this surface's polygon.
================
*/
void SetupSurfaceConnectivity(msurface_t *surf)
{
    int             i, j, lindex;
    temp_connect_t  *tempEdge;

    if (surf->numedges > 50)
    {
        Con_Printf ("to many edges %i\n", surf->numedges);
    }

    for (i=0 ; i<surf->numedges ; i++)
    {
        lindex = currentmodel->surfedges[surf->firstedge + i];
        tempEdge = tempEdges+abs(lindex);
        surf->polys->neighbours[i] = NULL;

        for (j=0; j<tempEdge->used; j++)
        {
            if (tempEdge->poly[j] != surf->polys)
            {
                surf->polys->neighbours[i] = tempEdge->poly[j];
            }
        }
    }
}

/*
================
PENTA:
PrintTempEdges
================
*/
void PrintTempEdges(void)
{
    int             i;
    temp_connect_t  *tempEdge;

    for (i=0 ; i<currentmodel->numedges ; i++)
    {
        tempEdge = tempEdges + i;

        Con_Printf("moord en brand %d\n",tempEdge->used);
    }
}

/*
================
BuildPolyFromSurface

Creer polygons van de lijst van surfaces om
gemakkelijk aan opengl te geven
================
*/
void BuildPolyFromSurface (msurface_t *fa)
{
    int			    i, lindex, lnumverts;
    medge_t		    *pedges, *r_pedge;
    float		    *vec;
    glpoly_t	    *poly;
    temp_connect_t  *tempEdge;
    float		    tex[2];
    float		    light[2];

// reconstruct the polygon
    pedges = currentmodel->edges;
    lnumverts = fa->numedges;

    //
    // draw texture <= wat mag dit comment me betekenen?
    //
    poly = Hunk_Alloc (sizeof(glpoly_t));

    //PENTA: reserve space for neighbour pointers
    //PENTA: FIXME: pointers don't need to be 4 bytes
    poly->neighbours = Hunk_Alloc (lnumverts * 4);

    poly->next = fa->polys;
    poly->flags = fa->flags;
    fa->polys = poly;
    poly->numverts = lnumverts;
    poly->firstvertex = R_GetNextVertexIndex();

    for (i=0 ; i<lnumverts ; i++)
    {
        lindex = currentmodel->surfedges[fa->firstedge + i];

        if (lindex > 0)
        {
            r_pedge = &pedges[lindex];
            vec = r_pcurrentvertbase[r_pedge->v[0]].position;
        }
        else
        {
            r_pedge = &pedges[-lindex];
            vec = r_pcurrentvertbase[r_pedge->v[1]].position;
        }
        tex[0] = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
        tex[0] /= fa->texinfo->texture->width;

        tex[1] = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
        tex[1] /= fa->texinfo->texture->height;

        //
        // lightmap texture coordinates
        //
        light[0] = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3];
        light[0] -= fa->texturemins[0];
        light[0] += fa->light_s*16;
        light[0] += 8;
        light[0] /= BLOCK_WIDTH*16; //fa->texinfo->texture->width;

        light[1] = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3];
        light[1] -= fa->texturemins[1];
        light[1] += fa->light_t*16;
        light[1] += 8;
        light[1] /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height;

        //Push back the new vertex
        R_AllocateVertexInTemp(vec, tex, light);

        //PENTA: Store in the tempedges table that this polygon uses the edge
        tempEdge = tempEdges + abs(lindex);

        if (tempEdge->used < 2)
        {
            tempEdge->poly[tempEdge->used]  = poly;
            tempEdge->used++;
        }
        else
        {
            Con_Printf ("BuildSurfaceDisplayList: Edge used by more than 2 surfaces\n");
        }
    }
    poly->numverts = lnumverts;
}

/*
========================
GL_CreateSurfaceLightmap
========================
*/
void GL_CreateSurfaceLightmap (msurface_t *surf)
{
    int		smax, tmax;
    byte	*base;

    if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) return;

    smax = (surf->extents[0] >> 4) + 1;
    tmax = (surf->extents[1] >> 4) + 1;

    surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t);
    base = lightmaps + surf->lightmaptexturenum * 1 * BLOCK_WIDTH * BLOCK_HEIGHT;
    base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * 1;
    R_BuildLightMap (surf, base, BLOCK_WIDTH * 1);
}

/*
==================
GL_BuildLightmaps

Builds the lightmap texture
with all the surfaces from all brush models
==================
*/
void GL_BuildLightmaps (void)
{
    int		i, j;
    model_t	*m;

    memset (allocated, 0, sizeof(allocated));

    r_framecount = 1;		// no dlightcache

    if (!lightmap_textures)
    {
        lightmap_textures = texture_extension_number;
        texture_extension_number += MAX_LIGHTMAPS;
    }

    // Default Lightmap brightness
    if (!lightmap_brightness)
    {
        lightmap_brightness = 0.3f;
    }

    // dont get cocky you !!!
    if (lightmap_brightness > 1.0f)
    {
        lightmap_brightness = 1.0f;
    }
    else if (lightmap_brightness < 0.0f)
    {
        lightmap_brightness = 0.0f;
    }

    for (j=1 ; j<MAX_MODELS ; j++)
    {
        m = cl.model_precache[j];
        if (!m) break;
        if (ISTURBTEX(m->name)) continue;

        r_pcurrentvertbase = m->vertexes;
        currentmodel = m;

        //PENTA: Allocate storage for our edge table
        tempEdges = Hunk_TempAlloc(m->numedges * sizeof(temp_connect_t));

        //clear tempedges
        for (i=0; i<m->numedges; i++)
        {
            tempEdges[i].used = 0;
            tempEdges[i].poly[0] = NULL;
            tempEdges[i].poly[1] = NULL;
        }

        for (i=0 ; i<m->numsurfaces ; i++)
        {
            GL_CreateSurfaceLightmap (m->surfaces + i);
            if ( m->surfaces[i].flags & SURF_DRAWTURB ) continue;
            BuildPolyFromSurface (m->surfaces + i);
        }

        //PENTA: we now have the connectivity in tempEdges now store it in the polygons
        for (i=0 ; i<m->numsurfaces ; i++)
        {
            if ( m->surfaces[i].flags & SURF_DRAWTURB ) continue;
            if ( m->surfaces[i].flags & SURF_DRAWSKY ) continue;
            SetupSurfaceConnectivity (m->surfaces + i);
        }
    }
    Con_Printf("Connectivity calculated\n");

    /*
    PENTA: Normalize texture coordinate s/t's since we now use them as tangent space
    */
    for (j=1 ; j<MAX_MODELS ; j++)
    {
        m = cl.model_precache[j];
        if (!m) break;
        if (ISTURBTEX(m->name)) continue;

        for (i=0; i<m->numtexinfo; i++)
        {
            Normalize(m->texinfo[i].vecs[0]);
            Normalize(m->texinfo[i].vecs[1]);
        }
    }

    //
    // upload all lightmaps that were filled
    //
    for (i=0 ; i<MAX_LIGHTMAPS ; i++)
    {
        if (!allocated[i][0]) break;		// no more used
        GL_Bind(lightmap_textures + i);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexImage2D (GL_TEXTURE_2D, 0, 1, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, lightmaps + i * BLOCK_WIDTH * BLOCK_HEIGHT * 1);
    }
}

